#include "prekompil.h"
#include <libsmbclient.h>
#include "skaner.h"
/*
 * Nie podoba mi sie tutaj funkcja main - sprawdzic sensownosc
 * ogolnie to dziala troche inaczej jak FTP - przynajmniej tak sie wydaje
 * na pierwszy rzut oka.
 * Dodatkowo chyba bylo pisane przez innego czlowieka poniewaz jest nawet
 * logowanie do pliku i wydaje sie ze latwiej jest to zdebugowac.
 */

typedef vector<string> Katalogi;
char workgroup[9];
char username[6];
char password[2];
/*
* NIE wiem do czego to ma sluzyc jednakze wymagane jest do odpalenia funkcji w dokumentacji jest napisane ze
 Authentication callback function type (traditional method)
 *
 * Type for the the authentication function called by the library to
 * obtain authentication credentals

 Inicjalizacja przeniesiona na sam dol.
*/
static void
get_auth_data_fn(const char * pServer,
                 const char * pShare,
                 char * pWorkgroup,
                 int maxLenWorkgroup,
                 char * pUsername,
                 int maxLenUsername,
                 char * pPassword,
                 int maxLenPassword);
/*
static void smb_auth_fn(const char      *server,
        const char      *share,
	char            *wrkgrp, int wrkgrplen,
	char            *user,   int userlen,
	char            *passwd, int passwdlen){
    strncpy(wrkgrp, workgroup, wrkgrplen - 1); wrkgrp[wrkgrplen - 1] = 0;
    strncpy(user, username, userlen - 1); user[userlen - 1] = 0;
    strncpy(passwd, password, passwdlen - 1); passwd[passwdlen - 1] = 0;
}
*/

class SkanerNetBios : public ISkaner{
    protected:
	string komputer;
	string aktualny_katalog;
	unsigned poziom;
	int fd;
	bool polaczony;
	SMBCCTX * ctx;
	ostream & plik;
    public:
	SkanerNetBios(const string & komputer, ostream & plik_log): plik(plik_log){
	    strcpy(workgroup,"UstroNET");
	    strcpy(username, "guest");
    	    strcpy(password,"");
	    polaczony = false;
    	    this->komputer = komputer;
//	    plik = (ostream)plik_log;
    	}

	~SkanerNetBios(){
	    Rozlacz();
	}

	static void InicjalizujSkanerNetBios(){
	        smbc_init(get_auth_data_fn, 0);
	}


	bool Polacz(){
	    aktualny_katalog = "/";
            poziom = 0;
	    polaczony = true;
	    int smbTimeout = 60;
	    int one = 1;
	    if(!(ctx = smbc_new_context())){
		plik<<"nie udalo sie konteksta utworzyc\n";
    		return false;
	    }
	    /* STAD ZACZYNAM POPRAWIAC STARA BIBLIOTEKE SAMBY
	    * stare zostalo zakomentowane - cameleeck
	    */
	    plik << "Ustawiam debug = 10 \n";
	    smbc_setTimeout(ctx, smbTimeout);
	    smbc_setDebug(ctx,1);
	    //ctx->debug = 10;
	    plik << "Ustawiam AuthDATA \n";
	   // smbc_setFunctionAuthData(ctx, smb_auth_fn);
	    smbc_setFunctionAuthData(ctx, get_auth_data_fn);
	    //ctx->callbacks.auth_fn = smb_auth_fn;
	    if(!(smbc_init_context(ctx))){
		plik<<"nie udalo sie zainicjalizowac kontekstu "<<" "<<komputer<<endl;
		smbc_free_context(ctx,1);
		return false;
	    }
	    smbc_setOptionUrlEncodeReaddirEntries(ctx, 1);
	    //ctx->options.urlencode_readdir_entries = 1;

	    const char * x = ((string)"smb://" + komputer + (string)"/").c_str();
	    plik << "Proba polaczenia z komputerem: smb://" << komputer << "/" << endl;
	    //if((fd = smbc_opendir(((string)"smb://" + komputer + (string)"/").c_str())) < 0){

	    if((fd = smbc_opendir(x) )< 0){
	    	//perror("smbc_opendir");
	    	// printf("Nie mozna otworzyc katalogu: [%s] (%d:%s)\n",path, errno, strerror(errno));
		polaczony = false;
    		//plik<<"NIe udalo polaczyc: "<<strerror(errno)<<endl;;
		return polaczony;
	    }
            return polaczony;
        }

	void Rozlacz(){
	    if (polaczony){
        	polaczony = false;
        	//MODYFIKACJA - cameleeck
        	smbc_getFunctionPurgeCachedServers(ctx);
		//ctx->callbacks.purge_cached_fn(ctx);
		smbc_free_context(ctx, 1);
            }
	}

	bool PolaczPonownie(){
    	    Rozlacz();
            return Polacz();
	}
// MAGIC FUNCTION - Jakas wazna funkcyjka - cameleeck
	/*
	 * w zasadzie to nie wiem o co chodzi z ta struct stat st, nie wyglada by cos sie do niej pisalo
	 * jest tylko jedno miejsce gdzie jest do niej pisanie/czytane przy zwracaniu rozmiaru pliku
	 *
	 */
	Pliki PobierzPliki(ostream & plik_log){
    	    Pliki wynik;
	    struct smbc_dirent * dirent;
	    long long int size;
	    int i;

	    plik<<"poziom="<<poziom<<endl;

            if (poziom == 0){
		plik<<"bedziemy skanowac katalog glowny\n";   //tu

		while((dirent = smbc_readdir(fd))){
		    plik<<"1";
		    if(dirent->smbc_type == SMBC_FILE_SHARE){
			Plik pliczek;
			i = 0;
			plik<<"2";

			while(dirent->name[i] != '\0')
        		    i++;
    			if(dirent->name[i-1] == '$')
        		    continue;
			plik<<"3-"<<dirent->name<<endl;
			pliczek.nazwa.assign(dirent->name);
			pliczek.katalog = true;
			pliczek.rozmiar = 0;
			plik<<"przed push_back"<<endl;
			wynik.push_back(pliczek);
			plik<<"po push_back"<<endl;
		    }
		}

		smbc_closedir(fd);
	    }
	    else{
		plik<<"bedziemy skanowac katalog: "<<aktualny_katalog<<endl;   //tu
		// moja poprawka c-string szuk
		// char szuk[10000];
        	string szukanie = (string)"smb://" + komputer + aktualny_katalog;
        	//string szukanie = (string)"smb://10.3.2.171/Nagrania/";
		 //szuk = new char [szukanie.size()+1];
		// strcpy (szuk, szukanie.c_str());
		plik<<"szukanie="<<szukanie<<endl;
	        if(fd = smbc_opendir(szukanie.c_str()))
		//if(fd = smbc_opendir("smb://10.3.2.171/Nagrania/"))
		{
		    while((dirent = smbc_readdir(fd)) /*!= NULL*/){ // TUTAJ MI SIE NIE PODOBA - cameleeck
			plik<<"4";
			if (strcmp(dirent->name, "") == 0) continue;
    			if (strcmp(dirent->name, ".") == 0) continue;
    			if (strcmp(dirent->name, "..") == 0) continue;
			plik<<"5";
			Plik pliczek;
			pliczek.katalog = (dirent->smbc_type == SMBC_DIR)?true:false;
			pliczek.nazwa.assign(dirent->name);
			plik<<"plik="<<dirent->name<<endl;
			plik<<"plik="<<pliczek.nazwa<<endl;
			struct stat stdummy1;   // struktury stdummy są potrzebne, bo bez tego funkcja smbc_stat nadpisywała
			struct stat stdummy2;   // jakies sasiednie komorki pamieci i w efekcie watek umieral przy probie
			struct stat st;
			struct stat stdummy3;   // ponownego obiegu petli
			struct stat stdummy4;
			stdummy1.st_size=1;
			stdummy2.st_size=stdummy1.st_size++;  // operacje przeciwdzialajace wyrzuceniu stdummy
			stdummy3.st_size=stdummy2.st_size++;  // przez optymalizator kompilatora
			stdummy4.st_size=stdummy3.st_size++;
			plik<<"Dummy output"<<stdummy4.st_size++<<endl;
			st.st_size = 0;
			size = 0;
			/*if(smbc_stat((szukanie+pliczek.nazwa).c_str(), &st) < 0){
			    plik<<"nie udalo sie pobrac rozmiaru pliku: "<<szukanie+pliczek.nazwa<<endl;
	    		}else{
			    if(st.st_size < 0){
                    		//size = (long long int) UINT_MAX - (long long int) st.st_size;
				size = 0; //druga wersja ==> jebie nas to ze ktos nam zwraca ujemny rozmiar
				plik<<"rozmiar=zero"<<endl;
			}else{
				size = (long long int) st.st_size;
				plik<<"rozmiar:"<<size<<endl;
			    }
			} */

			st.st_size = 123;
			plik<<"przed smbc"<<endl;
				// TUTAJ JEST COS ROBIONE Z st
			int res = smbc_stat((szukanie+pliczek.nazwa).c_str()/*"smb://10.3.2.171/Nagrania/cyfrowa_piosenka.3"*/, &st);
			plik<<"po smbc"<<endl;

			if( res < 0 )
			{
			    plik<<"nie udalo sie pobrac rozmiaru pliku: "<<szukanie+pliczek.nazwa<<endl;
	    		}
			else
			{
			    if(st.st_size < 0)
			    {
                    		//size = (long long int) UINT_MAX - (long long int) st.st_size;
				size = 0; //druga wersja ==> jebie nas to ze ktos nam zwraca ujemny rozmiar
				plik<<"rozmiar=zero"<<endl;
			    }
			    else
			    {
				size = (long long int) st.st_size;
				plik<<"rozmiar:"<<size<<endl;
			    }
			}



			pliczek.rozmiar = (unsigned long long int) size;
			plik<<"rozmiar w bazie: "<<pliczek.rozmiar<<" rozmiar w stat: "<<st.st_size<<endl;
			plik<<"bedzie push_back"<<endl;
			wynik.push_back(pliczek);
			plik<<"po push"<<endl;
		    }
		    plik<<"przed smbc_closedir()"<<endl;
            	    smbc_closedir(fd);
		    plik<<"po smbc_closedir()"<<endl;
               }
	       else   //tu
	         plik<<"Nie udalo sie otworzyc katalogu: "<<szukanie<<" "<<endl;   //tu
	       //delete [] szuk;
            }
	    plik<<"return wynik;"<<endl;
            return wynik;
         }

      bool WyjdzZKatalogu(ostream & plik_log)
         {
         poziom--;
         int pozycja = aktualny_katalog.size() - 2;
         while ((pozycja >= 0) && (aktualny_katalog[pozycja] != '/'))
            pozycja--;
         aktualny_katalog = aktualny_katalog.substr(0, pozycja+1);
	 plik_log<< "Leave... "  << aktualny_katalog<< endl;
         return true;
         }

      bool WejdzDoKatalogu(const string & katalog, ostream & plik_log)
         {
         poziom++;
         aktualny_katalog += katalog;
         aktualny_katalog += "/";
	 plik<< "Enter : " << aktualny_katalog<<endl;   //tu
         return true;
         }
   };


ISkaner * OtworzNetBios(const string & komputer, ostream & plik){
    SkanerNetBios * skaner = new SkanerNetBios(komputer, plik);
    if (skaner->Polacz())
        return skaner;
    else{
        delete skaner;
        return 0;
    }
}
/*
 * Czesc z sprawdzeniem maksymalnej_ilosci_prob jest w zasadzie nie potrzebna poniewaz ustawione jest 1 proba
 * -cameleeck
 */
void SkanujKatalog(unsigned katalog, unsigned poziom_katalogu, ostream & plik_log, ISkaner * skaner, Katalogi & katalogi, IBaza * baza){
    const int maksymalna_ilosc_prob = 1;
    plik_log<<"Wywolanie SkanujKatalog()"<<endl;
    if (poziom_katalogu == 32)
        return;
    int numer_proby = 0;
    bool sukces = false;
    ISkaner::Pliki pliki;
    while (!sukces && numer_proby < maksymalna_ilosc_prob){
	try{
	 //skanuje dany katalog
	    plik_log<<"--- Proba "<<numer_proby<<endl;
            pliki = skaner->PobierzPliki(plik_log);
            sukces = true;
	    plik_log<<"--- Po probie "<<numer_proby<<endl;
        }
	//na wypadke kiedy nie dalo by sie skanowac
        catch (WyjatekSkanera){
	    plik_log<<"WyjatekSkanera!";
    	    numer_proby++;
            while (numer_proby < maksymalna_ilosc_prob){
        	if (skaner->PolaczPonownie())
                break;
        	numer_proby++;
		plik_log<<" "<<numer_proby;
            }
	}
    }
    if (!sukces)
        throw Wyjatek("Blad podczas pobierania listy plikow");

    unsigned i;
    for (i = 0; i < pliki.size(); i++)
	//prawdopodobnie jezeli w nazwie nie ma '|'
        if (pliki[i].nazwa.find('|') == pliki[i].nazwa.npos)
            if (pliki[i].katalog){
	    //jezeli wpis jest katalogiem
	    //dodajemy do bazy
        	unsigned numer_wpisu = baza->DodajWpis(katalog, pliki[i].nazwa + "/", 0);
        	sukces = false;
        	numer_proby = 0;
        	while (!sukces && numer_proby < maksymalna_ilosc_prob){
            	    try{
		  //wchodzimy do tego katalogu
                	sukces = skaner->WejdzDoKatalogu(pliki[i].nazwa, plik_log);
                    }
            	    catch (WyjatekSkanera){
		  //jakby cos sie nie udalo to probujemy sie polaczyc ponownie
			plik_log<<"cos sie nie udalo musimy wchodzic jeszcze raz\n";
                	numer_proby++;
                	while (!sukces && numer_proby < maksymalna_ilosc_prob){
                    	    if (skaner->PolaczPonownie())
                    		break;
                    	    numer_proby++;
            		}
        	    }
            	}
        	if (sukces){
	       //wrzucamy go do listy katalogow zeby w przypadku zerwania polaczenie moc tam wrocic
            	    katalogi.push_back(pliki[i].nazwa);
	       //skanujemy katalog
            	    SkanujKatalog(numer_wpisu, poziom_katalogu+1, plik_log, skaner, katalogi, baza);
	       //zeskanowalismy wiec mozemy go wyciagnac z listy
            	    katalogi.pop_back();
            	    sukces = false;
            	    numer_proby = 0;
            	    try{
                	sukces = skaner->WyjdzZKatalogu(plik_log);
                    }
            	    catch (WyjatekSkanera){
			plik_log<<"cos nie wyszlo przy wychodzeniu\n";
                	numer_proby++;
                	while (!sukces && numer_proby < maksymalna_ilosc_prob){
                    	    if (skaner->PolaczPonownie())
                    	    break;
                    	numer_proby++;
//		     printf("Blad wyjscia z kat\n");
                        }
                    }
            	    if (numer_proby >= maksymalna_ilosc_prob)
                    throw Wyjatek("Blad podczas opuszczania katalogu");
                }
    	    }
	    //jezeli pozycja jest plikiem dodajemy go do bazy
            else
        	baza->DodajWpis(katalog, pliki[i].nazwa, pliki[i].rozmiar);
}

int main(int argc, char * argv[]){
    int ppid = getppid();
      ISkaner * skaner;
	string nazwa_komputera;
	string ip;
	string ipek; // dodane bo w IP dostawalem cala linijke z hosts
	string katalog;
      IBaza * baza = 0;
      Katalogi katalogi;
  	ofstream plik_log;
  	nazwa_komputera.assign(argv[1]);
	ip.assign(argv[2]);
	/*
	* Wyciagniecie samego IP
	*/
	katalog.assign(argv[3]);
	 unsigned koniec_ip = ip.find(' ');
	 ipek = ip.substr(0, koniec_ip);
//  	cout<<nazwa_komputera
    plik_log.open((nazwa_komputera + "." + "netbios.log").c_str(), ios_base::trunc | ios_base::out);
    plik_log<<"[samba] start:"<<nazwa_komputera<<endl;
    plik_log << "IP Komputera: "<< ipek << endl;
    try{
	//otwiwera polaczenie  z kompem
//	cout<<"start: "<<nazwa_komputera<<endl;
	SkanerNetBios::InicjalizujSkanerNetBios();
	plik_log<<"[samba] po: inicjalizujSkanerNetBios - "<<nazwa_komputera<<endl;
        skaner = OtworzNetBios(ipek, plik_log);
        if (skaner){
	    plik_log<<"[samba] polaczylem sie z: "<<nazwa_komputera<<endl;
            baza = OtworzBazePlik(katalog + nazwa_komputera + ".netbios");
	    plik_log<<"[samba] po OtworzBazePlik(): "<<katalog + nazwa_komputera;
            SkanujKatalog(UINT_MAX, 0, plik_log, skaner, katalogi, baza);
	    plik_log<<"[samba] po SkanujKatalog()"<<endl;
        }
        plik_log << skaner << endl;
	plik_log<<"[samba] koniec: "<<nazwa_komputera<<endl;
    }
    catch (Wyjatek w){
	plik_log << "[samba] Wyjatek: "<<w.opis << endl;
    }
    plik_log<<"[samba] Koniec."<<endl;
    plik_log.close();
    delete baza;
    delete skaner;
    baza = 0;
    skaner = 0;
    return 0;
}

static void
get_auth_data_fn(const char * pServer,
                 const char * pShare,
                 char * pWorkgroup,
                 int maxLenWorkgroup,
                 char * pUsername,
                 int maxLenUsername,
                 char * pPassword,
                 int maxLenPassword)
                 {
    strncpy(pWorkgroup, workgroup, maxLenWorkgroup - 1); pWorkgroup[maxLenWorkgroup - 1] = 0;
    strncpy(pUsername, username, maxLenUsername - 1); pUsername[maxLenUsername - 1] = 0;
    strncpy(pPassword, password, maxLenPassword - 1); pPassword[maxLenPassword - 1] = 0;
    return;
                 }
